﻿using Acme.BookStore.Navigates;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using SqlSugar;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TenantManagement;

namespace Acme.BookStore.Data
{
    public class BookStoreDbMigrationService : ITransientDependency
    {
        public ILogger<BookStoreDbMigrationService> Logger { get; set; }

        private readonly IDataSeeder _dataSeeder;
        private readonly IEnumerable<IBookStoreDbSchemaMigrator> _dbSchemaMigrators;
        private readonly ITenantRepository _tenantRepository;
        private readonly ICurrentTenant _currentTenant;

        private readonly SqlSugarScope db; // 核心对象：拥有完整的SqlSugar全部功能 单例

        public BookStoreDbMigrationService(
            IDataSeeder dataSeeder,
            IEnumerable<IBookStoreDbSchemaMigrator> dbSchemaMigrators,
            ITenantRepository tenantRepository,
            ICurrentTenant currentTenant,
            SqlSugarScope Db)
        {
            _dataSeeder = dataSeeder;
            _dbSchemaMigrators = dbSchemaMigrators;
            _tenantRepository = tenantRepository;
            _currentTenant = currentTenant;
            db = Db;
            Logger = NullLogger<BookStoreDbMigrationService>.Instance;
        }

        public async Task MigrateAsync()
        {
            Logger.LogInformation("Started database migrations...");

            await MigrateDatabaseSchemaAsync();
            await SeedDataAsync();

            Logger.LogInformation($"Successfully completed host database migrations.");

            var tenants = await _tenantRepository.GetListAsync(includeDetails: true);

            var migratedDatabaseSchemas = new HashSet<string>();
            foreach (var tenant in tenants)
            {
                if (!tenant.ConnectionStrings.Any())
                {
                    continue;
                }

                using (_currentTenant.Change(tenant.Id))
                {
                    var tenantConnectionStrings = tenant.ConnectionStrings
                        .Select(x => x.Value)
                        .ToList();

                    if (!migratedDatabaseSchemas.IsSupersetOf(tenantConnectionStrings))
                    {
                        await MigrateDatabaseSchemaAsync(tenant);

                        migratedDatabaseSchemas.AddIfNotContains(tenantConnectionStrings);
                    }

                    await SeedDataAsync(tenant);
                }

                Logger.LogInformation($"Successfully completed {tenant.Name} tenant database migrations.");
            }

            Logger.LogInformation("Successfully completed database migrations.");
        }

        private async Task MigrateDatabaseSchemaAsync(Tenant tenant = null)
        {
            Logger.LogInformation(
                $"Migrating schema for {(tenant == null ? "host" : tenant.Name + " tenant")} database...");

            foreach (var migrator in _dbSchemaMigrators)
            {
                await migrator.MigrateAsync();
            }
        }

        private async Task SeedDataAsync(Tenant tenant = null)
        {
            Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed...");

            await _dataSeeder.SeedAsync(tenant?.Id);
        }

        public async Task MigrateSqlSugarAsync()
        {
            Logger.LogInformation("Started SqlSugar database migrations...");

            await MigrateSqlSugarDatabaseSchemaAsync();
            await SeedSqlSugarDataAsync();

            Logger.LogInformation("Successfully completed database migrations.");
        }

        private Task MigrateSqlSugarDatabaseSchemaAsync(Tenant tenant = null)
        {
            Logger.LogInformation($"Migrating SqlSugar schema for {(tenant == null ? "host" : tenant.Name + " tenant")} database...");

            var sugarTables = typeof(BookStoreDomainModule).Assembly.GetTypes().Where(x => x.GetCustomAttributes(typeof(SugarTable), false).Length > 0).ToArray();
            
            db.CodeFirst.InitTables(sugarTables);
          
            var tables = db.DbMaintenance.GetTableInfoList();
            Logger.LogInformation($"Successfully completed SqlSugar database migrations.");

            return Task.CompletedTask;
        }

        private async Task SeedSqlSugarDataAsync(Tenant tenant = null)
        {
            Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed SqlSugarData...");

            if (!db.Queryable<Room>().Any())
            {
                await db.Insertable(new Room() { RoomId = 1, RoomName = "北大001室", SchoolId = 1 }).ExecuteCommandAsync();
                await db.Insertable(new Room() { RoomId = 2, RoomName = "北大002室", SchoolId = 1 }).ExecuteCommandAsync();
                await db.Insertable(new Room() { RoomId = 3, RoomName = "北大003室", SchoolId = 1 }).ExecuteCommandAsync();
                await db.Insertable(new Room() { RoomId = 4, RoomName = "清华001厅", SchoolId = 2 }).ExecuteCommandAsync();
                await db.Insertable(new Room() { RoomId = 5, RoomName = "清华002厅", SchoolId = 2 }).ExecuteCommandAsync();
                await db.Insertable(new Room() { RoomId = 6, RoomName = "清华003厅", SchoolId = 2 }).ExecuteCommandAsync();
            }
            if (!db.Queryable<School>().Any())
            {
                await db.Insertable(new School() { SchoolId = 1, SchoolName = "北大" }).ExecuteCommandAsync();
                await db.Insertable(new School() { SchoolId = 2, SchoolName = "清华" }).ExecuteCommandAsync();
            }
            if (!db.Queryable<Student>().Any())
            {
                await db.Insertable(new Student() { StudentId = 1, SchoolId = 1, Name = "北大jack" }).ExecuteCommandAsync();
                await db.Insertable(new Student() { StudentId = 2, SchoolId = 1, Name = "北大tom" }).ExecuteCommandAsync();
                await db.Insertable(new Student() { StudentId = 3, SchoolId = 2, Name = "清华jack" }).ExecuteCommandAsync();
                await db.Insertable(new Student() { StudentId = 4, SchoolId = 2, Name = "清华tom" }).ExecuteCommandAsync();
            }
            if (!db.Queryable<Teacher>().Any())
            {
                await db.Insertable(new Teacher() { SchoolId = 1, Id = 1, Name = "北大老师01" }).ExecuteCommandAsync();
                await db.Insertable(new Teacher() { SchoolId = 1, Id = 2, Name = "北大老师02" }).ExecuteCommandAsync();
                await db.Insertable(new Teacher() { SchoolId = 2, Id = 3, Name = "清华老师01" }).ExecuteCommandAsync();
                await db.Insertable(new Teacher() { SchoolId = 2, Id = 4, Name = "清华老师02" }).ExecuteCommandAsync();
            }
            if (!db.Queryable<StuBook>().Any())
            {
                await db.Insertable(new StuBook() { BookId = 1, Name = "book1", StudentId = 1 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 1, Name = "book1", StudentId = 2 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 1, Name = "book1", StudentId = 3 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 1, Name = "book1", StudentId = 4 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 2, Name = "book2", StudentId = 1 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 2, Name = "book2", StudentId = 2 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 2, Name = "book2", StudentId = 3 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 2, Name = "book2", StudentId = 4 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 3, Name = "book3", StudentId = 1 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 3, Name = "book3", StudentId = 2 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 3, Name = "book3", StudentId = 3 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 4, Name = "book4", StudentId = 1 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 5, Name = "book5", StudentId = 1 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 5, Name = "book5", StudentId = 2 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 5, Name = "book5", StudentId = 3 }).ExecuteCommandAsync();
                await db.Insertable(new StuBook() { BookId = 5, Name = "book5", StudentId = 4 }).ExecuteCommandAsync();
            }
            Logger.LogInformation($"Successfully completed seed SqlSugarData.");
        }
    }
}